package gui;

import file.manager.FileManagerStartException;
import gui.chatarea.MultiChatArea;
import gui.dialog.ChatAproposDialog;
import gui.dialog.ChatConfigDialog;
import gui.dialog.ChatConnectDialog;
import gui.dialog.ConnectionListener;
import gui.userinput.ChatInput;
import gui.userslist.ChatUsersList;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;

import javax.swing.BoxLayout;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JSplitPane;
import javax.swing.UIManager;

import ui.StateObserver;
import utils.Configuration;

import network.ListenerBindException;
import network.SendSignalException;
import network.config.UnsupportedProtocol;
import network.config.NetworkConfiguration.PortType;

import controller.MessageController;
import controller.UserAlreadyConnected;

public class ChatWindow extends JFrame implements ConnectionListener {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	/* Controller. */
	private MessageController msgCtrl ;
	/* Components. */
	private MultiChatArea multiChatArea ;
	private ChatUsersList chatUsersList ;
	private ChatInput chatInput ;
	/* Panel. */
	private JSplitPane upperPanel ;
	private JSplitPane globalPanel ;
	/* Dialog. */
	private JDialog dialogConnect ;
	private JDialog aproposDialog ;
	
	private final String WINDOW_TITLE = "M&M's Chat" ;
	
	private WindowListener windowAdapter = new WindowAdapter() {

		@Override
		public void windowClosed(WindowEvent e) {
			try {
				ChatWindow.this.msgCtrl.performDisconnect() ;
			}
			catch (Exception ex) {
				ex.printStackTrace();
			}
		}

		@Override
		public void windowClosing(WindowEvent e) {	
			try {
				ChatWindow.this.msgCtrl.performDisconnect() ;
			}
			catch (Exception ex) {

			}
		}
		
		@Override
		public void windowOpened(WindowEvent e) {
			ChatWindow.this.dialogConnect.setVisible(true) ;
		}
	} ;
	
	private ComponentListener componentAdapter = new ComponentAdapter () {

		@Override
		public void componentResized(ComponentEvent e) {
			if (isVisible()) {
				ChatWindow.this.upperPanel.setDividerLocation(0.8) ;
				ChatWindow.this.globalPanel.setDividerLocation(0.9) ;
			}
		}

		@Override
		public void componentShown(ComponentEvent e) {
			if (isVisible()) {
				ChatWindow.this.upperPanel.setDividerLocation(0.8) ;
				ChatWindow.this.globalPanel.setDividerLocation(0.9) ;
			}
		}
	} ;
	
	public ChatWindow () throws SendSignalException, ListenerBindException, UserAlreadyConnected, FileManagerStartException {
		super () ;
		/* Dialog and listener */
		this.dialogConnect = new ChatConnectDialog(this, this) ;
		this.aproposDialog = new ChatAproposDialog(this) ;
		addWindowListener(this.windowAdapter) ;
		addComponentListener(this.componentAdapter) ;
		
		/* Create the controller. */
		this.msgCtrl = new MessageController() ;
		
		/* Configure Window. */
		setSize(new Dimension(500,500)) ;
		setMinimumSize(new Dimension (400,200)) ;
		setDefaultCloseOperation(EXIT_ON_CLOSE) ;
		setTitle(WINDOW_TITLE) ;
		setIconImage(Toolkit.getDefaultToolkit().getImage(Configuration.getImageFolder() + File.separator + "connect_img.png")) ;
		
		/* Create component. */
		this.chatInput = new ChatInput(this.msgCtrl) ;
		this.multiChatArea = new MultiChatArea(this.msgCtrl) ;
		this.chatUsersList = new ChatUsersList(this.msgCtrl, this.multiChatArea) ;
		this.multiChatArea.addUserTargetedListener(this.chatUsersList) ;
		
		/* Place component. */
		setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS)) ;
		this.upperPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, this.multiChatArea, this.chatUsersList) ;
		this.globalPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, this.upperPanel, this.chatInput) ;
		this.globalPanel.setAlignmentX(LEFT_ALIGNMENT) ;
		ChatStateBar stateBar = new ChatStateBar() ;
		stateBar.setAlignmentX(LEFT_ALIGNMENT) ;
		ChatConfigDialog configDialog = new ChatConfigDialog(this, this.msgCtrl) ;
		ChatMenuBar menuBar = new ChatMenuBar(this, this.dialogConnect, this.aproposDialog, configDialog, this.multiChatArea, this.msgCtrl) ;
		setJMenuBar(menuBar) ;
		add(this.globalPanel) ;	
		add(stateBar) ;
		
		/* Add listener... */
		this.chatUsersList.addUserSelectionListener(this.chatInput) ;
		this.chatUsersList.addUserSelectionListener(menuBar) ;
		
		/* Add observers to controller... youhou ! */
		this.msgCtrl.addStateObserver(stateBar) ;
		this.msgCtrl.addStateObserver(this.chatInput) ;
		this.msgCtrl.addStateObserver(this.multiChatArea) ;
		this.msgCtrl.addStateObserver((StateObserver) getJMenuBar()) ;
		this.msgCtrl.addMessageObserver(this.multiChatArea) ;
		this.msgCtrl.addTransferFileObserver(this.multiChatArea) ;
		this.msgCtrl.addUsersListObserver(this.multiChatArea) ;
		this.msgCtrl.addUsersListObserver(this.chatUsersList.getModel()) ;
		this.msgCtrl.addNetworkConfigObserver(configDialog) ;
		this.msgCtrl.addNetworkConfigObserver(this.chatUsersList) ;
		
		/* Resize component. */
		this.componentAdapter.componentResized (null) ;
	}

	@Override
	public void actionConnect (ConnectionEvent ce) {
		if (ce.accepted()) {
			try {
				this.msgCtrl.performConnect(ce.username()) ;
				this.dialogConnect.setVisible(false) ;
				this.chatInput.requestFocus();
			} 
			catch (SendSignalException e) {
				JOptionPane.showMessageDialog(this, "Impossible de se connecter : Vérifiez que vous êtes bien connecté à un réseau.", 
						"Erreur de connexion", JOptionPane.ERROR_MESSAGE) ;
			} 
			catch (ListenerBindException e) {
				JOptionPane.showMessageDialog(this, "Impossible de se connecter : Un autre programme utilise le port UDP " + this.msgCtrl.getPort(PortType.UDP_RECEIVE) + ".", 
						"Erreur de connexion", JOptionPane.ERROR_MESSAGE) ;
			} 
			catch (UserAlreadyConnected e) {

			} 
			catch (FileManagerStartException e) {
				JOptionPane.showMessageDialog(this, "Impossible de se connecter : Un autre programme utilise le port TCP " + this.msgCtrl.getPort(PortType.TCP_ACCEPT) + ".", 
						"Erreur de connexion", JOptionPane.ERROR_MESSAGE) ;
			} catch (UnsupportedProtocol e) {
				JOptionPane.showMessageDialog(this, "Impossible de se connecter : Protocole " + e.getProtocol() + " non supporté. ", 
						"Erreur de connexion", JOptionPane.ERROR_MESSAGE) ;
			}
		}
	}
	
	/* Different LookAndFeel, the last is the prefered one.
	 */
	private static final String[] LOOK_AND_FEELS = {
		"com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel",
		"de.javasoft.plaf.synthetica.SyntheticaSimple2DLookAndFeel",
		UIManager.getSystemLookAndFeelClassName()
	} ;

	public static void main (String[] args) throws SendSignalException, ListenerBindException, UserAlreadyConnected, FileManagerStartException {
		/* Change le look des fenêtres. */
		for (String look : LOOK_AND_FEELS) {
			try {
				UIManager.setLookAndFeel(look) ;
			} 
			catch (Exception e) {
			}
		}
		ChatWindow mainWindow = new ChatWindow(); 
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize() ;
		mainWindow.setLocation((int)(screenSize.getWidth() - mainWindow.getWidth())/3, 
				(int)(screenSize.getHeight() - mainWindow.getHeight())/3) ;
		mainWindow.setVisible(true) ;
	}
	
}
